/*
 * Copyright (C) 2018 Zhengzhou grandtech Technology CO.,LTD.
 * All rights reserved.
 * Official Web Site: http://www.grandtech.com.
 * Developer Web Site: http://open.grandtech.com.
 */

package com.grandtech.oss.api;

import cn.hutool.core.util.StrUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.grandtech.oss.common.annotation.NotLogin;
import com.grandtech.oss.common.pojo.Pagination;
import com.grandtech.oss.common.pojo.RestfulMessage;
import com.grandtech.oss.domain.FileBinaryRequest;
import com.grandtech.oss.domain.FileBinaryResponse;
import com.grandtech.oss.exception.AssemblerException;
import com.grandtech.oss.exception.ErrorCable;
import com.grandtech.oss.exception.ErrorConstant;
import com.grandtech.oss.module.entity.OSSMaterialInfoResult;
import com.grandtech.oss.module.model.OSSAppInfo;
import com.grandtech.oss.module.model.OSSDeveloper;
import com.grandtech.oss.module.model.OSSInformation;
import com.grandtech.oss.module.model.OSSMaterialInfo;
import com.grandtech.oss.service.*;
import org.apache.commons.codec.net.URLCodec;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.StringReader;
import java.lang.reflect.Type;
import java.util.List;

// import com.grandtech.oss.common.annotation.NotLogin;

/***
 *
 * @since:oss-server 1.0
 * @author <a href="mailto:yanggh@foxmail.com">yanggh@foxmail.com</a> 
 * 2018/05/30 10:47
 */

@RestController
@RequestMapping("/api/v1/oss/material")
public class MaterialApplication extends RootApis {
    /**
     * @apiDefine material material（文件对象存储）
     */

    private Log log = LogFactory.get();

    @Autowired
    MaterialService materialService;

    @Autowired
    OSSMaterialInfoService ossMaterialInfoService;

    @Autowired
    OSSDeveloperService ossDeveloperService;

    @Autowired
    OSSAppInfoService ossAppInfoService;

    @Autowired
    OSSInformationService ossInformationService;

    @Autowired
    ExcelService excelService;

    /**
     * @api {GET} /api/v1/oss/material/queryByPage queryByPage（获取文件列表）
     * @apiName queryByPage
     * @apiGroup material
     * @apiVersion 1.0.0
     * @apiParam {Integer} page 当前页
     * @apiParam {Integer} rows 每页行数
     * @apiParam {String} sortOrder 排序字段
     * @apiParamExample {json} Request-Example：
     * {
     * "rows": 10,
     * "page": 1,
     * "sortOrder": "asc"
     * }
     * @apiSuccess {json} success 返回数据
     * @apiSuccessExample {json} Success-Response:
     * {
     * "code": 8200,
     * "count": 3,
     * "current_page": 1,
     * "message": null,
     * "page_size": 10,
     * "total_page": 1,
     * "data": [{
     * "appId": "5234b8d4494b496fb8fcd218f85610b7",
     * "appname": "yanggh",
     * "byteStr": "32.0KB",
     * "createTime": "2018-12-05 17:09:29",
     * "fromIp": "0:0:0:0:0:0:0:1",
     * "id": "57e99ddb50dd4a158956d7d4483e855a",
     * "lastModifiedTime": "2018-12-05 17:09:29",
     * "len": 32768,
     * "name": "meilc",
     * "originalName": "response.xls",
     * "storePath": "/test/cms/201812/05/57e99ddb50dd4a158956d7d4483e855a.xls",
     * "type": "xls",
     * "url": "http://localhost/files/test/cms/201812/05/57e99ddb50dd4a158956d7d4483e855a.xls",
     * "userId": "386607787ada4676a52cf78e1b15311a"
     * }]
     * }
     */
    @GetMapping("/queryByPage")
    public Pagination<OSSMaterialInfoResult> queryByPage(OSSMaterialInfo ossMaterialInfo,
                                                         @RequestParam(value = "page", defaultValue = "1") Integer current_page,
                                                         @RequestParam(value = "rows", defaultValue = "10") Integer page_size) {
        return ossMaterialInfoService.queryByPage(ossMaterialInfo, current_page, page_size);
    }

    /***
     * @api {GET} /api/v1/oss/material/exportExcel exportExcel（下载Excel）
     * @apiName exportExcel
     * @apiGroup material
     * @apiVersion 1.0.0
     * @apiSuccess success 下载Excel
     */
    @GetMapping("/exportExcel")
    public void exportExcel(HttpServletRequest request, HttpServletResponse response) {
        try {
            response.setContentType("application/vnd.ms-excel");
            String fileName = System.currentTimeMillis() + "-oss-server.xls";
            response.setHeader("Content-disposition", "attachment;filename=" + fileName);
            Workbook workbook = excelService.exportMaterials();
            OutputStream outputStream = response.getOutputStream();
            workbook.write(outputStream);
            outputStream.flush();
            outputStream.close();
        } catch (Exception e) {
            log.error(e);

        }
    }

    /***
     * @api {POST} /api/v1/oss/material/createDir createDir（新建文件路径）
     * @apiName createDir
     * @apiGroup material
     * @apiVersion 1.0.0
     *
     * @apiParam {String} [dir] 路径
     * @apiParam {String} pro 应用
     * @apiParamExample {json} Request-Example：
     * {
     *     "dir": "/currentfoldername",
     *     "pro": "newfoldername"
     * }
     *
     * @apiSuccess {json} success 返回数据
     * @apiSuccessExample {json} Success-Response:
     * {
     *     "code": 8200,
     *     "data": null,
     *     "message": "Success"
     * }
     */
    @PostMapping(value = "/createDir")
    public RestfulMessage createDirectory(@RequestParam(value = "dir", required = false) String dir,
                                          @RequestParam(value = "pro") String pro) {
        RestfulMessage restfulMessage = new RestfulMessage();
        try {
            String root = ossInformationService.queryOne().getRoot();
            StringBuffer dirStr = new StringBuffer();
            dirStr.append(root).append(dir);
            validateProjectName(pro);
            dirStr.append("/").append(pro);
            File file = new File(dirStr.toString());
            createDirectoryQuietly(file);
            file.setExecutable(false);
            file.setWritable(false);
            file.setReadOnly();
            successResultCode(restfulMessage);
        } catch (Exception e) {
            restfulMessage = wrapperException(e);
        }
        return restfulMessage;
    }

    /***
     * @api {POST} /api/v1/oss/material/uploadByBinary uploadByBinary（通过二进制字节码上传文件-旧版）
     * @apiName uploadByBinary
     * @apiGroup material
     * @apiVersion 1.0.0
     *
     * @apiParam {String} appid 开发者appid
     * @apiParam {String} appsecret 开发者appsecret
     * @apiParam {String} project 应用编码
     * @apiParam {String} module 模块（应用中某个模块存储的文件夹，允许多层文件夹：cb/nh）
     * @apiParam {MultipartFile[]} files 上传文件
     * @apiParamExample {json} Request-Example：
     * {
     *     "project":"application_code",
     *     "appid":"oss0s4w4i",
     *     "appsecret":"o4s1u844",
     *     "module":"application_model",
     *     "files":[
     *     {
     *         "original_name":"test.png",
     *         "file":"F://test.png",
     *         "media_type":"png"
     *     }
     *     ]
     * }
     *
     * @apiSuccess {json} success 返回数据
     * @apiSuccessExample {json} Success-Response:
     * {
     *   "code": 8200,
     *   "message": "Success",
     *   "data": [
     *     {
     *       "id": "164332db37ad409ba9dde439cd7171a2",
     *       "url": "http://localhost/files/app_meilc_2/model_2/201812/14/164332db37ad409ba9dde439cd7171a2.jpg",
     *       "store": "/app_meilc_2/model_2/201812/14/164332db37ad409ba9dde439cd7171a2.jpg",
     *       "objType": "jpg",
     *       "originalName": "高通8cx.jpg",
     *       "byteLength": 45647,
     *       "byteToStr": "44.58KB"
     *     }
     *   ]
     * }
     */
    @NotLogin
    @PostMapping(value = "/uploadByBinary", produces = "application/json;charset=UTF-8")
    public RestfulMessage upload(HttpEntity<String> entity) {
        RestfulMessage restfulMessage = new RestfulMessage();
        try {
            log.info("/uploadByBinary...");
            String bodyStr = entity.getBody();
            String decodeBodyStr = new String(URLCodec.decodeUrl(bodyStr.getBytes("UTF-8")), "UTF-8");
            StringReader strReader = new StringReader(decodeBodyStr);
            JsonReader reader = new JsonReader(strReader);
            reader.setLenient(true);
            JsonElement je = new JsonParser().parse(reader);
            if (je == null || je.isJsonNull() || !je.isJsonObject() || ("null".equals(je.toString())) || (je.toString() == null)) {
                throw new AssemblerException(new ErrorCable(ErrorConstant.REQUEST_PARAMS_NOT_VALID, "not any data"));
            }
            //获取project
            JsonObject req = je.getAsJsonObject();
            //获取appid
            JsonElement appIdEle = req.get("appid");
            JsonElement appSecretEle = req.get("appsecret");
            assertJsonNotEmpty(appIdEle, "appid can't be empty ");
            assertJsonNotEmpty(appSecretEle, "appsecret can't be empty ");
            //获取模块名称
            JsonElement moduleEle = req.get("module");
            String moduleStr = "";
            if (moduleEle != null && !moduleEle.isJsonNull() && moduleEle.isJsonPrimitive()) {
                moduleStr = moduleEle.getAsString();
            }
            JsonElement projectEle = req.get("project");
            JsonElement files = req.get("files");
            assertJsonNotEmpty(projectEle, "project name can't be empty!!!");
            assertJsonNotEmpty(files, "files can't be empty!!!");
            if (!files.isJsonArray()) {
                throw new AssemblerException(new ErrorCable(ErrorConstant.REQUEST_PARAMS_NOT_VALID, "files must be array"));
            }
            OSSDeveloper ossDeveloper = ossDeveloperService.queryByAppid(appIdEle.getAsString(), appSecretEle.getAsString());
            assertArgumentNotEmpty(ossDeveloper, "appid or appsecret is invalid");
            String projectFilePathName = projectEle.getAsString();
            validateProjectName(projectFilePathName);
            //判断文件夹code是否相等
            List<OSSAppInfo> ossAppInfos = ossAppInfoService.queryByDevIds(ossDeveloper.getId());
            if (ossAppInfos == null || ossAppInfos.size() == 0) {
                throw new AssemblerException(new ErrorCable(ErrorConstant.REQUEST_PARAMS_NOT_VALID, "You do not have permission to upload files"));
            }
            OSSAppInfo ossApp = null;
            boolean flag = false;
            for (OSSAppInfo ossAppInfo : ossAppInfos) {
                if (StrUtil.equalsIgnoreCase(ossAppInfo.getCode(), projectFilePathName)) {
                    flag = true;
                    ossApp = ossAppInfo;
                    break;
                }
            }
            if (!flag) {
                throw new AssemblerException(new ErrorCable(ErrorConstant.REQUEST_PARAMS_NOT_VALID, "You do not have permission to upload files"));
            }
            OSSInformation ossInformation = ossInformationService.queryOne();
            String root = ossInformation.getRoot();
            //验证文件夹规则,不能包含特殊字符
            File file = new File(root);
            createDirectoryQuietly(file);
            StringBuffer path = new StringBuffer();
            path.append(file.getAbsolutePath());
            path.append(File.separator);
            path.append(projectFilePathName);
            if (StrUtil.isNotEmpty(moduleStr)) {
                if (!moduleStr.startsWith("/")) {
                    path.append("/");
                }
                path.append(moduleStr);
            }
            log.info("path:{}", path);
            File projectFile = new File(path.toString());
            if (!projectFile.exists()) {
                throw new AssemblerException(new ErrorCable(ErrorConstant.AUTHENTICATION_FAILED, "You do not have operating authority for this directory " + projectFilePathName + ", or the directory was not created"));
            }
            createDirectoryQuietly(projectFile);
            Type typeOf = new TypeToken<List<FileBinaryRequest>>() {
            }.getType();
            List<FileBinaryRequest> materials = new Gson().fromJson(files, typeOf);
            for (FileBinaryRequest fileBinaryRequest : materials) {
                assertArgumentNotEmpty(fileBinaryRequest.getMediaType(), "media_type is requried ...");
                assertArgumentNotEmpty(fileBinaryRequest.getFile(), "Not found file bytes, but it is requried ...");
                //fileBinaryRequest.setFileBytes(material.getFileBytes().replaceAll(" ", "+"));
                fileBinaryRequest.setFile(fileBinaryRequest.getFile().replaceAll(" ", "+"));
            }
            List<FileBinaryResponse> fileBinaryResponseList = materialService.saveAndStore(ossInformation, ossDeveloper, ossApp, projectFile, materials);
            restfulMessage.setData(fileBinaryResponseList);
            successResultCode(restfulMessage);
        } catch (Exception e) {
            restfulMessage = wrapperException(e);
        }
        return restfulMessage;
    }

    /***
     * @api {POST} /api/v1/oss/material/{project}/uploadMaterial uploadMaterial（上传文件-常用）
     * @apiName uploadMaterial
     * @apiGroup material
     * @apiVersion 1.0.0
     * @apiDescription 上传文件，路径中带project的写法
     *
     * @apiParam {String} appid 开发者appid
     * @apiParam {String} appsecret 开发者appsecret
     * @apiParam {String} project 应用编码
     * @apiParam {String} module 模块（应用中某个模块存储的文件夹，允许多层文件夹：cb/nh）
     * @apiParam {MultipartFile[]} files 上传文件（支持多个）
     * @apiParamExample {json} Request-Example：
     * {
     *     "project":"application_code",
     *     "appid":"oss0s4w4i",
     *     "appsecret":"o4s1u844",
     *     "module":"application_model",
     *     "files":[
     *     {
     *         "original_name":"test.png",
     *         "file":"F://test.png",
     *         "media_type":"png"
     *     }
     *     ]
     * }
     *
     * @apiSuccess {json} success 返回数据
     * @apiSuccessExample {json} Success-Response:
     * {
     *   "code": 8200,
     *   "message": "Success",
     *   "data": [
     *     {
     *       "id": "164332db37ad409ba9dde439cd7171a2",
     *       "url": "http://localhost/files/app_meilc_2/model_2/201812/14/164332db37ad409ba9dde439cd7171a2.jpg",
     *       "store": "/app_meilc_2/model_2/201812/14/164332db37ad409ba9dde439cd7171a2.jpg",
     *       "objType": "jpg",
     *       "originalName": "高通8cx.jpg",
     *       "byteLength": 45647,
     *       "byteToStr": "44.58KB"
     *     }
     *   ]
     * }
     */
    @Deprecated
    @NotLogin
    @PostMapping("{project}/uploadMaterial")
    public RestfulMessage uploadMaterial(@PathVariable(value = "project") String project,
                                         @RequestParam String module,
                                         @RequestParam String appid,
                                         @RequestParam String appsecret,
                                         @RequestParam(value = "file") MultipartFile[] files) {
        RestfulMessage restfulMessage = new RestfulMessage();
        try {
            restfulMessage = uploadFileByForm(project, appid, appsecret, module, files);
        } catch (Exception e) {
            restfulMessage = wrapperException(e);
        }
        return restfulMessage;
    }

    /***
     * @api {POST} /api/v1/oss/material/uploadMaterialNonProUrl uploadMaterialNonProUrl（上传文件）
     * @apiName uploadMaterialNonProUrl
     * @apiGroup material
     * @apiVersion 1.0.0
     * @apiDescription 上传文件，路径中无project的写法
     *
     * @apiParam {String} appid 开发者appid
     * @apiParam {String} appsecret 开发者appsecret
     * @apiParam {String} project 应用编码
     * @apiParam {String} module 模块（应用中某个模块存储的文件夹，允许多层文件夹：cb/nh）
     * @apiParam {MultipartFile[]} files 上传文件
     * @apiParamExample {json} Request-Example：
     * {
     *     "project":"application_code",
     *     "appid":"oss0s4w4i",
     *     "appsecret":"o4s1u844",
     *     "module":"application_model",
     *     "files":[
     *     {
     *         "original_name":"test.png",
     *         "file":"F://test.png",
     *         "media_type":"png"
     *     }
     *     ]
     * }
     *
     * @apiSuccess {json} success 返回数据
     * @apiSuccessExample {json} Success-Response:
     * {
     *   "code": 8200,
     *   "message": "Success",
     *   "data": [
     *     {
     *       "id": "164332db37ad409ba9dde439cd7171a2",
     *       "url": "http://localhost/files/app_meilc_2/model_2/201812/14/164332db37ad409ba9dde439cd7171a2.jpg",
     *       "store": "/app_meilc_2/model_2/201812/14/164332db37ad409ba9dde439cd7171a2.jpg",
     *       "objType": "jpg",
     *       "originalName": "高通8cx.jpg",
     *       "byteLength": 45647,
     *       "byteToStr": "44.58KB"
     *     }
     *   ]
     * }
     */
    @NotLogin
    @PostMapping("/uploadMaterialNonProUrl")
    public RestfulMessage uploadMaterialNonProUrl(@RequestParam(value = "project", required = false) String project,
                                                  @RequestParam(value = "module", required = false) String module,
                                                  @RequestParam(value = "appid", required = false) String appid,
                                                  @RequestParam(value = "appsecret", required = false) String appsecret, @RequestParam(value = "file") MultipartFile[] files) {
        RestfulMessage restfulMessage = new RestfulMessage();
        try {
            restfulMessage = uploadFileByForm(project, appid, appsecret, module, files);
        } catch (Exception e) {
            restfulMessage = wrapperException(e);
        }
        return restfulMessage;
    }


    private RestfulMessage uploadFileByForm(String project, String appid, String appsecret, String module, MultipartFile[] files) throws IOException {
        RestfulMessage restfulMessage = new RestfulMessage();
        assertArgumentNotEmpty(project, "project can't be empty!!!");
        assertArgumentNotEmpty(appid, "appid can't be empty ");
        assertArgumentNotEmpty(appsecret, "appsecret can't be empty ");
        OSSDeveloper ossDeveloper = ossDeveloperService.queryByAppid(appid, appsecret);
        assertArgumentNotEmpty(ossDeveloper, "appid or appsecret is invalid");
        //判断文件夹code是否相等
        List<OSSAppInfo> ossAppInfos = ossAppInfoService.queryByDevIds(ossDeveloper.getId());
        if (ossAppInfos == null || ossAppInfos.size() == 0) {
            throw new AssemblerException(new ErrorCable(ErrorConstant.REQUEST_PARAMS_NOT_VALID, "You do not have permission to upload files"));
        }
        boolean flag = false;
        OSSAppInfo ossApp = null;
        for (OSSAppInfo ossAppInfo : ossAppInfos) {
            if (StrUtil.equalsIgnoreCase(ossAppInfo.getCode(), project)) {
                flag = true;
                ossApp = ossAppInfo;
                break;
            }
        }
        if (!flag) {
            throw new AssemblerException(new ErrorCable(ErrorConstant.REQUEST_PARAMS_NOT_VALID, "You do not have permission to upload files"));
        }
        validateProjectName(project);
        OSSInformation ossInformation = ossInformationService.queryOne();
        String root = ossInformation.getRoot();
        //验证文件夹规则,不能包含特殊字符
        File file = new File(root);
        createDirectoryQuietly(file);
        StringBuffer path = new StringBuffer();
        path.append(file.getAbsolutePath());
        path.append(File.separator);
        path.append(project);
        if (StrUtil.isNotEmpty(module)) {
            if (!module.startsWith("/")) {
                path.append("/");
            }
            path.append(module);
        }
        log.info("path:{}", path);
        File projectFile = new File(path.toString());
            /*if (!projectFile.exists()){
                throw new AssemblerException(new ErrorCable(ErrorConstant.AUTHENTICATION_FAILED,"You do not have operating authority for this directory "+project+", or the directory was not created"));
            }*/
        createDirectoryQuietly(projectFile);
        List<FileBinaryResponse> fileBinaryResponseList = materialService.saveAndStore(ossInformation, ossDeveloper, ossApp, projectFile, files);
        restfulMessage.setData(fileBinaryResponseList);
        successResultCode(restfulMessage);
        return restfulMessage;
    }

    /***
     * @api {POST} /api/v1/oss/material/uploadSys uploadSys（上传文件）
     * @apiName uploadSys
     * @apiGroup material
     * @apiVersion 1.0.0
     * @apiDescription 直接输入存储路径（为空时放在根目录），无需指定开发者和应用，一般不推荐使用
     *
     * @apiParam {String} [dir] 路径
     * @apiParam {MultipartFile[]} files 上传文件
     * @apiParamExample {json} Request-Example：
     * {
     *     "dir":"",
     *     "files":[
     *     {
     *         "original_name":"test.png",
     *         "file":"F://test.png",
     *         "media_type":"png"
     *     }
     *     ]
     * }
     *
     * @apiSuccess {json} success 返回数据
     * @apiSuccessExample {json} Success-Response:
     * {
     *   "code": 8200,
     *   "message": "Success",
     *   "data": [
     *     {
     *       "id": "80e53458bc094d30bf99a2e3a2a9bd85",
     *       "url": "http://localhost/files//80e53458bc094d30bf99a2e3a2a9bd85.jpg",
     *       "store": "//80e53458bc094d30bf99a2e3a2a9bd85.jpg",
     *       "objType": null,
     *       "originalName": null,
     *       "byteLength": 0,
     *       "byteToStr": null
     *     }
     *   ]
     * }
     */
    @PostMapping("/uploadBySys")
    public RestfulMessage uploadSys(@RequestParam(value = "dir", required = false) String dir, @RequestParam(value = "file") MultipartFile[] files) {
        RestfulMessage restfulMessage = new RestfulMessage();
        try {
            OSSInformation ossInformation = ossInformationService.queryOne();
            String root = ossInformation.getRoot();
            //验证文件夹规则,不能包含特殊字符
            File file = new File(root);
            createDirectoryQuietly(file);
            StringBuffer path = new StringBuffer();
            path.append(file.getAbsolutePath());
            if (StrUtil.isNotEmpty(dir)) {
                if (!dir.startsWith("/")) {
                    path.append(File.separator);
                }
                path.append(dir);
            }
            log.info("path:{}", path);
            File projectFile = new File(path.toString());
            createDirectoryQuietly(projectFile);
            List<FileBinaryResponse> fileBinaryResponseList = materialService.saveAndStoreBySys(ossInformation, projectFile, files);
            restfulMessage.setData(fileBinaryResponseList);
            successResultCode(restfulMessage);
        } catch (Exception e) {
            restfulMessage = wrapperException(e);
        }
        return restfulMessage;
    }
}
